package beautiful.butterfly.schedule.zookeeper;

import beautiful.butterfly.schedule.Constants;
import beautiful.butterfly.schedule.Uuids;
import beautiful.butterfly.schedule.strategy.MachineInfo;
import beautiful.butterfly.schedule.strategy.RunntimeStrategy;
import beautiful.butterfly.schedule.strategy.ScheduleFactory;
import beautiful.butterfly.schedule.strategy.Strategy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.Getter;
import lombok.Setter;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooKeeper;

import java.io.Writer;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Manager {
    @Setter
    @Getter
    private ZooKeeperManager zooKeeperManager;
    @Setter
    @Getter
    private String strategysRootPath;
    @Setter
    @Getter
    private String machineInfosRootPath;
    @Setter
    @Getter
    private Gson gson;

    public Manager(ZooKeeperManager zooKeeperManager) throws Exception {

        this.gson = new GsonBuilder().registerTypeAdapter(Timestamp.class, new TimestampTypeAdapter()).setDateFormat("yyyy-MM-dd HH:mm:ss").create();
        //
        this.zooKeeperManager = zooKeeperManager;
        //
        this.strategysRootPath = this.zooKeeperManager.getRootPath() + Constants.$strategys;
        this.machineInfosRootPath = this.zooKeeperManager.getRootPath() + Constants.$machineinfos;
        if (this.getZooKeeper().exists(this.strategysRootPath, false) == null) {
            ZooKeepers.createPath(getZooKeeper(), this.strategysRootPath, this.zooKeeperManager.getAclList(), CreateMode.PERSISTENT);
        }
        if (this.getZooKeeper().exists(this.machineInfosRootPath, false) == null) {
            ZooKeepers.createPath(getZooKeeper(), this.machineInfosRootPath, this.zooKeeperManager.getAclList(), CreateMode.PERSISTENT);
        }
    }

    //
    public void createStrategy(Strategy strategy) throws Exception {
        String strategyPath = this.strategysRootPath + Constants.slash + strategy.getStrategyName();
        String value = this.gson.toJson(strategy);
        if (this.getZooKeeper().exists(strategyPath, false) == null) {
            this.getZooKeeper().create(strategyPath, value.getBytes(), this.zooKeeperManager.getAclList(), CreateMode.PERSISTENT);
        } else {
            throw new Exception("" + strategy.getStrategyName() + "has existed");
        }
    }

    //
    public void pause(String strategyName) throws Exception {
        Strategy strategy = this.loadStrategy(strategyName);
        strategy.setStatus(Strategy.status_pause);
        this.updateStrategy(strategy);
    }

    public void resume(String strategyName) throws Exception {
        Strategy strategy = this.loadStrategy(strategyName);
        strategy.setStatus(Strategy.status_resume);
        this.updateStrategy(strategy);
    }

    public void updateStrategy(Strategy strategy)
            throws Exception {
        String strategyPath = this.strategysRootPath + Constants.slash + strategy.getStrategyName();
        String value = this.gson.toJson(strategy);
        if (this.getZooKeeper().exists(strategyPath, false) == null) {
            this.getZooKeeper().create(strategyPath, value.getBytes(), this.zooKeeperManager.getAclList(), CreateMode.PERSISTENT);
        } else {
            this.getZooKeeper().setData(strategyPath, value.getBytes(), -1);
        }

    }

    //
    public void deleteStrategy(String taskType) throws Exception {
        deleteStrategy(taskType, false);
    }


    public void deleteStrategy(String strategy, boolean isForce) throws Exception {
        String strategyPath = this.strategysRootPath + Constants.slash + strategy;
        if (isForce == false && this.getZooKeeper().getChildren(strategyPath, null).size() > 0) {
            throw new Exception("" + strategy + "" + "");
        }
        ZooKeepers.directoryStructureDelete(this.getZooKeeper(), strategyPath);
    }
    //

    public List<Strategy> loadStrategyList() throws Exception {

        List<Strategy> strategyList = new ArrayList<Strategy>();
        List<String> strategyNameList = this.getZooKeeper().getChildren(this.strategysRootPath, false);
        Collections.sort(strategyNameList);
        for (String strategyName : strategyNameList) {
            strategyList.add(this.loadStrategy(strategyName));
        }
        return strategyList;
    }

    /**
     * 经常用到的方法
     */
    public Strategy loadStrategy(String strategyName)
            throws Exception {
        String strategyPath = this.strategysRootPath + Constants.slash + strategyName;
        if (this.getZooKeeper().exists(strategyPath, false) == null) {
            return null;
        } else {
            String value = new String(this.getZooKeeper().getData(strategyPath, false, null));
            Strategy strategy = this.gson.fromJson(value, Strategy.class);
            return strategy;
        }

    }
    //

    public List<String> registerMachineInfo(ScheduleFactory scheduleFactory) throws Exception {

        if (scheduleFactory.getMachineId() == null) {
            String uuid = Uuids.getUuid();
            //String machineId  ip+scheduleFactory.getHostName()+uuid
            String machineId = scheduleFactory.getIp() + Constants.$ + scheduleFactory.getHostName() + Constants.$ + uuid;
            String machineIdPath = this.machineInfosRootPath + Constants.slash + machineId + Constants.$;
            this.getZooKeeper().create(machineIdPath, null, this.zooKeeperManager.getAclList(), CreateMode.EPHEMERAL_SEQUENTIAL);
            scheduleFactory.setMachineId(machineId);
        } else {
            String path = this.machineInfosRootPath + Constants.slash + scheduleFactory.getMachineId();
            if (this.getZooKeeper().exists(path, false) == null) {
                this.getZooKeeper().create(path, null, this.zooKeeperManager.getAclList(), CreateMode.EPHEMERAL);
            }
        }
        //
        List<String> strategyNameList = new ArrayList<String>();
        for (Strategy strategy : loadStrategyList())//all strategy in zk
        {
            boolean isFind = false;
            if (Strategy.status_pause.equalsIgnoreCase(strategy.getStatus()) == false //
                    &&//
                    strategy.getIpList() != null)//

            {
                for (String ip : strategy.getIpList()) {//strategy/ips
                    if (ip.equals("127.0.0.1") || ip.equalsIgnoreCase("localhost") ||//
                            ip.equals(scheduleFactory.getIp()) || ip.equalsIgnoreCase(scheduleFactory.getHostName())) {//ip一致
                        //strategy is not the same
                        String machineIdPath = this.strategysRootPath +//
                                Constants.slash + strategy.getStrategyName() + //
                                Constants.slash + scheduleFactory.getMachineId();
                        if (this.getZooKeeper().exists(machineIdPath, false) == null) {
                            this.getZooKeeper().create(machineIdPath, null, this.zooKeeperManager.getAclList(), CreateMode.EPHEMERAL);
                        }
                        //
                        isFind = true;
                        break;
                    }
                }
            }
            //删除不必存在的数据
            if (isFind == false) {
                String machineIdPath = this.strategysRootPath + //
                        Constants.slash + strategy.getStrategyName() +//
                        Constants.slash + scheduleFactory.getMachineId();//
                if (this.getZooKeeper().exists(machineIdPath, false) != null) {
                    ZooKeepers.directoryStructureDelete(this.getZooKeeper(), machineIdPath);
                    strategyNameList.add(strategy.getStrategyName());
                }
            }
        }
        return strategyNameList;
    }

    /**
     * 卸载
     */
    public void unRregisterMachineInfo(ScheduleFactory scheduleFactory) throws Exception {
        for (String strategyName : this.getZooKeeper().getChildren(this.strategysRootPath, false)) {
            String path = this.strategysRootPath + Constants.slash + strategyName + Constants.slash + scheduleFactory.getMachineId();
            if (this.getZooKeeper().exists(path, false) != null) {
                ZooKeepers.directoryStructureDelete(this.getZooKeeper(), path);
            }
        }
    }

    public RunntimeStrategy loadRunntimeStrategy(String strategyName, String machineId) throws Exception {
        // /strategyName/serverId=runntimeStrategy
        String path = this.strategysRootPath + Constants.slash + strategyName + Constants.slash + machineId;
        if (this.getZooKeeper().exists(path, false) == null) {
            return null;
        } else {
            byte[] bytes = this.getZooKeeper().getData(path, false, null);
            if (bytes != null) {
                String value = new String(bytes);
                RunntimeStrategy runntimeStrategy = this.gson.fromJson(value, RunntimeStrategy.class);
                //
                if (null == runntimeStrategy) {
                    throw new Exception();
                }
                if (null == runntimeStrategy.getStrategyName()) {
                    throw new Exception();
                }
                if (null == runntimeStrategy.getMachineId()) {
                    throw new Exception();
                }
                return runntimeStrategy;
                //
            } else {
                RunntimeStrategy runntimeStrategy = new RunntimeStrategy();
                runntimeStrategy.setStrategyName(strategyName);
                runntimeStrategy.setMachineId(machineId);
                runntimeStrategy.setRequestNum(0);
                runntimeStrategy.setMessage("");
                return runntimeStrategy;
            }
        }


    }


    public List<RunntimeStrategy> loadRunntimeStrategyByStrategyName(String strategyName) throws Exception {
        List<RunntimeStrategy> runntimeStrategyList = new ArrayList<RunntimeStrategy>();

        if (this.getZooKeeper().exists(this.strategysRootPath + Constants.slash + strategyName, false) == null) {
            return runntimeStrategyList;
        }
        // strategyName/machineIdList
        List<String> machineIdList = this.getZooKeeper().getChildren(this.strategysRootPath + Constants.slash + strategyName, false);
        Collections.sort(machineIdList, new Comparator<String>() {
            public int compare(String leftString, String rightString) {
                return leftString.substring(leftString.lastIndexOf(Constants.$) + 1).compareTo(
                        rightString.substring(rightString.lastIndexOf(Constants.$) + 1));
            }
        });

        for (String machineId : machineIdList) {
            runntimeStrategyList.add(loadRunntimeStrategy(strategyName, machineId));
        }
        return runntimeStrategyList;
    }

    /**
     * 获取当前machineId下面的策略
     */
    public List<RunntimeStrategy> loadRunntimeStrategyListByMachineId(String machineId) throws Exception {
        List<RunntimeStrategy> runntimeStrategyList = new ArrayList<RunntimeStrategy>();
        List<String> strategyNameList = this.getZooKeeper().getChildren(this.strategysRootPath, false);
        Collections.sort(strategyNameList);
        for (String strategyName : strategyNameList) {
            if (this.getZooKeeper().exists(this.strategysRootPath + Constants.slash + strategyName + Constants.slash + machineId, false) != null) {
                runntimeStrategyList.add(loadRunntimeStrategy(strategyName, machineId));
            }
        }
        return runntimeStrategyList;
    }


    public void updateRunntimeStrategyReqestNum(String strategyName, String machineId, int requestNum) throws Exception {
        String path = this.strategysRootPath + Constants.slash + strategyName + Constants.slash + machineId;
        RunntimeStrategy runntimeStrategy = null;
        if (this.getZooKeeper().exists(path, false) != null) {
            runntimeStrategy = this.loadRunntimeStrategy(strategyName, machineId);
        } else {
            runntimeStrategy = new RunntimeStrategy();
            runntimeStrategy.setStrategyName(strategyName);
            runntimeStrategy.setMachineId(machineId);
            runntimeStrategy.setRequestNum(0);//0->requestNum is the best
            runntimeStrategy.setMessage("");
        }
        runntimeStrategy.setRequestNum(requestNum);//设置请求数
        String value = this.gson.toJson(runntimeStrategy);
        this.getZooKeeper().setData(path, value.getBytes(), -1);
    }


    /**
     * 管理工厂
     */
    public MachineInfo loadMachineInfoInfo(String machineId) throws Exception {
        String path = this.machineInfosRootPath + Constants.slash + machineId;
        if (this.getZooKeeper().exists(path, false) == null) {
            throw new Exception(machineId);
        }
        byte[] bytes = this.getZooKeeper().getData(path, false, null);
        MachineInfo machineInfo = new MachineInfo();
        machineInfo.setMachineId(machineId);
        if (bytes == null) {
            machineInfo.setStart(true);
        } else {
            machineInfo.setStart(Boolean.parseBoolean(new String(bytes)));
        }
        return machineInfo;
    }

    //
    public List<MachineInfo> getMachineInfoList() throws Exception {

        List<MachineInfo> machineInfoList = new ArrayList<MachineInfo>();
        List<String> machineIdList = this.getZooKeeper().getChildren(this.machineInfosRootPath, false);
        Collections.sort(machineIdList, new Comparator<String>() {
            public int compare(String leftString, String rightString) {
                return leftString.substring(leftString.lastIndexOf(Constants.$) + 1).compareTo(
                        rightString.substring(rightString.lastIndexOf(Constants.$) + 1));
            }
        });
        for (String machineId : machineIdList) {
            MachineInfo machineInfo = new MachineInfo();
            machineInfo.setMachineId(machineId);
            byte[] bytes = this.getZooKeeper().getData(this.machineInfosRootPath + Constants.slash + machineId, false, null);
            if (bytes == null) {
                machineInfo.setStart(true);
            } else {
                machineInfo.setStart(Boolean.parseBoolean(new String(bytes)));
            }
            machineInfoList.add(machineInfo);
        }
        return machineInfoList;
    }

    public void updateManagerFactoryInfo(String machineId, boolean isStart) throws Exception {
        String path = this.machineInfosRootPath + Constants.slash + machineId;
        if (this.getZooKeeper().exists(path, false) == null) {
            throw new Exception(machineId);
        }
        this.getZooKeeper().setData(path, Boolean.toString(isStart).getBytes(), -1);
    }


    //
    public void importConfig(String config, Writer writer, boolean isUpdate)
            throws Exception {
        ConfigNode configNode = gson.fromJson(config, ConfigNode.class);
        if (configNode != null) {
            String configTypePath = configNode.getRootPath() + Constants.slash + configNode.getConfigType();
            ZooKeepers.createPath(getZooKeeper(), configTypePath, zooKeeperManager.getAclList(), CreateMode.PERSISTENT);
            String namePath = configTypePath + Constants.slash + configNode.getName();
            if (getZooKeeper().exists(namePath, false) == null) {
                writer.append("<font color=\"red\">\n</font>");
                getZooKeeper().create(namePath, configNode.getValue().getBytes(), zooKeeperManager.getAclList(), CreateMode.PERSISTENT);
            } else if (isUpdate) {
                writer.append("<font color=\"red\">\n</font>");
                getZooKeeper().setData(namePath, configNode.getValue().getBytes(), -1);
            } else {
                writer.append("<font color=\"red\">\n</font>");
            }
        }
        writer.append(configNode.toString());
    }

    public StringBuffer exportConfig(String rootPath, Writer writer)
            throws Exception {
        StringBuffer sb = new StringBuffer();
        for (String configType : new String[]{"$baseTaskType", "strategy"})//只导出这两个方面
        {
            if (configType.equals("$baseTaskType")) {
                writer.write("<h2></h2>\n");
            } else {
                writer.write("<h2></h2>\n");
            }
            String path = rootPath + Constants.slash + configType;
            List<String> nodeList = getZooKeeper().getChildren(path, false);
            for (int i = 0; i < nodeList.size(); i++) {
                String node = nodeList.get(i);
                ConfigNode configNode = new ConfigNode(rootPath, configType, node);
                configNode.setValue(new String(this.getZooKeeper().getData(path + Constants.slash + node, false, null)));
                sb.append(gson.toJson(configNode));
                sb.append("\n");
                writer.write(configNode.toString());
            }
            writer.write("\n\n");
        }
        if (sb.length() > 0) {
            String str = sb.toString();
            return new StringBuffer(str.substring(0, str.length() - 1));
        }
        return sb;
    }


    public void printTree(String path, Writer writer, String lineSplit)
            throws Exception {
        ZooKeepers.showInDirectoryStructureWay(this.getZooKeeper(), path, writer, lineSplit);
    }

    public void deleteTree(String path) throws Exception {
        ZooKeepers.directoryStructureDelete(this.getZooKeeper(), path);
    }

    public ZooKeeper getZooKeeper() throws Exception {
        return this.zooKeeperManager.getZooKeeper();
    }

    public String getRootPath() {
        return this.zooKeeperManager.getRootPath();
    }
}
